#include "recovery/fs/file/File.h"

#include "recovery/fs/file/FileDescriptor.h"
#include <unistd.h>
#include <sys/stat.h>
#include <sys/errno.h>
#include <fcntl.h>

//PosixImpl as Default
namespace recovery {
namespace fs {
namespace file {

bool File::exist() const{
    struct stat statbuf;
    if(0 == ::stat(path().c_str(), &statbuf)){
        return S_ISREG(statbuf.st_mode);
    }
    return false;
}
ErrnoStatus File::remove() const {
    return ::unlink(path().c_str()) == 0 ? ErrnoStatus::OK() : ErrnoStatus::Error(errno);
}
ErrnoStatus File::touch() const {
    int fd = ::open(path().c_str(), O_RDWR|O_CREAT, 0600);
    if(fd < 0){
        return ErrnoStatus::Error(errno);
    }
    ::close(fd);
    return ErrnoStatus::OK();
}
ErrnoStatus File::truncate(size_t size) const{
    return ::truncate(path().c_str(), size) == 0 ? ErrnoStatus::OK() : ErrnoStatus::Error(errno);
}
ErrnoStatus File::size(size_t & sz) const{
    struct stat statbuf;
    if(0 == ::stat(path().c_str(), &statbuf)){
        sz = statbuf.st_size;
        return ErrnoStatus::OK();
    }
    return ErrnoStatus::Error(errno);
}

static inline int toPosixfd(const FileDescriptor & fd){
    return static_cast<int>(reinterpret_cast<long>(fd.impl()));
}
static inline void * fromPosixfd(int fd){
    return reinterpret_cast<void*>(fd);
}

static ErrnoStatus _open(FileDescriptor & fd, const char * path , int oflag){
    int fdret = ::open(path, oflag);
    if(fdret< 0){
        return ErrnoStatus::Error(errno);
    }
    fd.set(fromPosixfd(fdret));
    return ErrnoStatus::OK();
}
static ErrnoStatus _create(FileDescriptor & fd, const char * path , int oflag, mode_t mode){
    int fdret = ::open(path, oflag|O_CREAT, mode);
    if(fdret< 0){
        return ErrnoStatus::Error(errno);
    }
    fd.set(fromPosixfd(fdret));
    return ErrnoStatus::OK();
}
ErrnoStatus File::openForAppend(FileDescriptor & fd) const{
    return _open(fd, path().c_str(), O_RDWR|O_APPEND);
}
ErrnoStatus File::openForRandomRead(FileDescriptor & fd) const{
    return _open(fd, path().c_str(), O_RDONLY);
}
ErrnoStatus File::openForRandomWrite(FileDescriptor & fd) const{
    return _open(fd, path().c_str(), O_RDWR);
}

ErrnoStatus File::createForAppend(FileDescriptor & fd) const{
    return _create(fd, path().c_str(), O_RDWR|O_APPEND, 0600);
}
ErrnoStatus File::createForRandomWrite(FileDescriptor & fd) const{
    return _create(fd, path().c_str(), O_RDWR, 0600);
}

ErrnoStatus File::close(const FileDescriptor & fd) const{
    return ::close(toPosixfd(fd)) == 0 ? ErrnoStatus::OK() : ErrnoStatus::Error(errno);
}

ErrnoStatus File::posixSeek(const FileDescriptor & fd, const int64_t offset, int whence) const{
    off_t newpos = ::lseek(toPosixfd(fd), offset, whence);
    if(newpos >= 0){
        return ErrnoStatus::OK();
    }
    return ErrnoStatus::Error(errno);
}

ErrnoStatus File::skip(const FileDescriptor & fd, const int64_t offset) const{
    return posixSeek(fd, offset, SEEK_CUR);
}
ErrnoStatus File::seekBegin(const FileDescriptor & fd, const size_t offset) const{
    return posixSeek(fd, offset, SEEK_SET);
}
ErrnoStatus File::seekEnd(const FileDescriptor & fd, const int64_t offset) const{
    return posixSeek(fd, offset, SEEK_END);
}
ErrnoStatus File::read(const FileDescriptor & fd, uint8_t * buf, const size_t len, size_t & readBytes) const{
    ssize_t ret = ::read(toPosixfd(fd), reinterpret_cast<char *>(buf), len);
    if(ret >= 0){
        readBytes = ret;
        return ErrnoStatus::OK();
    }
    return ErrnoStatus::Error(errno);
}

ErrnoStatus File::append(const FileDescriptor & fd, const uint8_t * buf, const size_t len, size_t & appendedBytes) const{
    return write(fd, buf, len, appendedBytes);
}
ErrnoStatus File::write(const FileDescriptor & fd, const uint8_t * buf, const size_t len, size_t & writedBytes) const{
    ssize_t ret = ::write(toPosixfd(fd), reinterpret_cast<const char*>(buf), len);
    if(ret >= 0){
        writedBytes = ret;
        return ErrnoStatus::OK();
    }
    return ErrnoStatus::Error(errno);
}

ErrnoStatus File::positionedRead(const FileDescriptor & fd, const size_t offset, uint8_t * buf, const size_t len, size_t & readBytes) const{
    ssize_t ret = ::pread(toPosixfd(fd), reinterpret_cast<char *>(buf), len, offset);
    if(ret >= 0){
        readBytes = ret;
        return ErrnoStatus::OK();
    }
    return ErrnoStatus::Error(errno);
}
ErrnoStatus File::positionedAppend(const FileDescriptor & fd, const size_t offset, const uint8_t * buf, const size_t len, size_t & appendedBytes) const{
    off_t newpos = ::lseek(toPosixfd(fd), offset, SEEK_SET);
    if(newpos >= 0){
        return write(fd, buf, len, appendedBytes);
    }
    return ErrnoStatus::Error(errno);
}
ErrnoStatus File::positionedWrite(const FileDescriptor & fd, const size_t offset, const uint8_t * buf, const size_t len, size_t writedBytes) const{
    ssize_t ret = ::pwrite(toPosixfd(fd), reinterpret_cast<const char*>(buf), len, offset);
    if(ret >= 0){
        writedBytes = ret;
        return ErrnoStatus::OK();
    }
    return ErrnoStatus::Error(errno);
}

ErrnoStatus File::sync(const FileDescriptor & fd) const{
    return ::fsync(toPosixfd(fd)) == 0 ? ErrnoStatus::OK() : ErrnoStatus::Error(errno);
}

} //end of namespace file
} //end of namespace fs
} //end of namespace recovery
